﻿using System;
using System.Diagnostics;
using System.IO;

namespace SfsTools.Core
{
    /// <summary>
    /// Base class for index area entries.
    /// </summary>
    internal abstract class IndexEntry : Object, IFsComponent
    {
        #region Properties

        /// <summary>
        /// Contains the buffer.
        /// </summary>
        protected readonly byte[] FBuffer;

        /// <summary>
        /// Contains the position of this <see cref="IFsComponent"/>.
        /// </summary>
        private long FPosition;

        /// <summary>
        /// Gets the index area entry type code.
        /// </summary>
        public byte Type
        {
            [DebuggerStepThrough]
            get
            {
                return FBuffer[0];
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Gets a <see cref="Stream"/> that can be used to read the contents of this entry.
        /// </summary>
        /// <returns></returns>
        [DebuggerStepThrough]
        public Stream GetContents()
        {
            return new MemoryStream(FBuffer, true);
        }

        /// <summary>
        /// Gets the position of this entry.
        /// </summary>
        /// <returns></returns>
        /// <remarks>Only to be used when this instance is no longer required, the entry will be replaced with something else.</remarks>
        [DebuggerStepThrough]
        protected long GetPosition()
        {
            return FPosition;
        }

        /// <summary>
        /// Gets the contents of this entry.
        /// </summary>
        /// <returns></returns>
        /// <remarks>Only to be used when this instance is no longer required, the entry will be replaced with something else.</remarks>
        [DebuggerStepThrough]
        public byte[] GetBuffer()
        {
            return FBuffer;
        }

        /// <summary>
        /// Accepts a visitor.
        /// </summary>
        /// <param name="visitor">The visitor.</param>
        public abstract void AcceptVisitor(IIndexEntryVisitor visitor);

        #endregion
        
        #region Constructors
        /// <overloads>Initializes a new instance of <see cref="IndexEntry"/>.</overloads>
        /// <summary>
        /// Initializes a new instance of <see cref="IndexEntry"/>.
        /// </summary>
        /// <param name="position">The position of this entry on the volume.</param>
        /// <param name="buffer">The buffer.</param>
        /// <exception cref="ArgumentNullException">The argument <paramref name="buffer"/> is <see langword="null"/>.</exception>
        [DebuggerStepThrough]
        protected IndexEntry(Int64 position, byte[] buffer)
            : base()
        {
            if (buffer == null)
                throw new ArgumentNullException("buffer");
            FBuffer = buffer;
            FPosition = position;
        }
        /// <summary>
        /// Initializes a new instance of <see cref="IndexEntry"/> from an existing instace.
        /// </summary>
        /// <param name="value">The instance to copy.</param>
        /// <exception cref="ArgumentNullException">The argument <paramref name="value"/> is <see langword="null"/>.</exception>
        [DebuggerStepThrough]
        protected IndexEntry(IndexEntry value)
            : base()
        {
            if (value == null)
                throw new ArgumentNullException("value");
            // ReSharper disable RedundantThisQualifier
            this.FBuffer = new byte[value.FBuffer.Length]; Array.Copy(value.FBuffer, this.FBuffer, FBuffer.Length);
            this.FPosition = value.FPosition;
            // ReSharper restore RedundantThisQualifier
        }
        #endregion

        #region IFsComponent Members

        /// <summary>
        /// Writes the component back to the volume.
        /// </summary>
        /// <param name="volume">The volume.</param>
        [DebuggerStepThrough]
        public void WriteTo(IVolume volume)
        {
            volume.Write(FPosition, FBuffer);
        }

        /// <summary>
        /// Writes the component back to the volume.
        /// </summary>
        /// <param name="volume">The volume.</param>
        [DebuggerStepThrough]
        void IFsComponent.WriteTo(IVolume volume)
        {
            volume.Write(FPosition, FBuffer);
        }

        #endregion
    }
}
